本篇主要接着上篇介绍Pipeline Recovery。

写Pipeline

当HDFS client执行一个写操作时,数据是以序列化的block形式写进去的。其中block又被分为很多packets,将packets发送到由一些dn组成的pipeline中,如下图所示:
pipeline流程

pipeline分为三个阶段:

  1. Pipeline setup
    client向pipeline发送一个Write_Block请求,pipeline中的最后一个DataNode往回发送一个ack。client收到ack之后,pipeline就处于setup状态,准备写入数据。
  2. Data streaming
    数据通过packets发送到pipeline中。client端缓存数据到buffer中,buffer满之后写入packet,等packet满之后,发送packet到pipeline中。如果client调用hflush()即使当前packet没有被写满,也会发送到pipeline中,并且下一个packet在收到当前packet的ack之前不会发送到pipeline中(上图中的packet2就是调用hflush)。
  3. Close(改变replica的状态为finalize并且关闭pipeline)
    client等待收到所有packet的ack之后,才会发送一个关闭请求。pipeline中的dn改变相应replica的状态为FINALIZED,并向nn报告。当nn收到replica的FINALIZED状态的个数满足最小副本数时,nn改变block的状态为COMPLETE。

Pipeline Recovery

无论在pipeline三个阶段中的哪个阶段,只要当pipeline中的dn发生error时,Pipeline Recovery就会被启动。

Recovery from Pipeline Setup Failure

1、当pipeline用来新增加一个block时,client放弃这个block,并向nn请求一个新的block和一组新的dn组重新组成一个pipeline。
2、当pipeline用来追加一个block时,client使用剩余的dn重建pipeline并且增加block的generation stamp。

Recovery from Data Streaming Failure

1、当pipeline中的dn发现error(checksum异常或者写到磁盘发生异常),发生error的dn关闭TCP/IP连接,移出pipeline。如果数据没有损坏,将缓存中的数据写入相应的block和checksum文件中(If the data is deemed not corrupted, it also writes buffered data to the relevant block and checksum (METADATA) files.)。
2、当client发现异常,client停止向pipeline中发送数据,使用剩余正常的dn重建一个新的pipeline。此时,block的所有replica都有一个新的GS。
3、client带着新的GS发送数据packet。有些dn如果已经接受到这些数据,则忽略这些packet并向pipel的下游发送这些packet。

Recovery from Close Failure

当pipeline处于close状态时,client发现故障,则利用剩余正常的dn重建pipeline。每个dn递增block的GS,如果replica不是finalized状态就将其改为finalized。

当pipeline中的一个dn发生故障,则将其故障节点从pipeline中移除。在pipeline recovery阶段,client可能需要利用剩余正常的节点重建pipeline。(在重建pipeline中,是否需要新的dn替代发生故障的dn,这个决策依赖DataNode替换策略,在下面的章节中介绍)复制监控线程将会检查block的复本,使其满足配置的副本因子。

DataNode Replacement Policy upon Failure

在pipeline recovery中,决定是否增加dn替换发生故障的dn的策略有4种。如下:

  • DISABLE: 禁止进行dn替换,并在服务器端抛出一个异常。类似于client端的NEVER。
  • NEVER: 当pipeline失败时不替换dn。(一般不这样搞)
  • DEFAULT: 基于以下规则替换dn:
    将配置的副本因子数设为r
    将pipeline中存活的dn数设为n
    如果r>=3并且满足下面任意一个条件才替换发生故障的dn
    floor(r/2) >= n; or (当hflush/append被调用并且r > n)
  • ALWAYS: Always是只要存在dn发生故障就会有新的dn进行替换。如果不能替换则失败。

如果想禁止这些策略,可以设置下面这个配置(默认值是true):
dfs.client.block.write.replace-datanode-on-failure.enable
假如上面的配置是true,则默认的替换策略是DEFAULT。由下面的属性控制:
dfs.client.block.write.replace-datanode-on-failure.policy
当使用DEFAULT或者ALWAYS时,如果pipeline中只有一个dn替换成功了,pipeline recovery将不会成功,并且client将不能在执行写操作。这个问题通过设置下面的属性进行解决:
dfs.client.block.write.replace-datanode-on-failure.best-effort
此属性默认是false。如果是默认值,client会一直尝试替换发生故障的dn直到满足特定的策略。如果设置为true,即使特定的策略不满足(在pipeline中只有一个dn成功,比要求的数量要少),client依然可以继续写。

原文地址